//
//  MTOverlayDataSourceFileDirectory.m
//  GoMap
//
//  Created by jsheriff on 12/13/10.
//  Copyright 2010 The MITRE Corporation. All rights reserved.
//

#import "MTOverlayDataSourceFileDirectory.h"
#import "MTOverlayTile.h"

#define TILE_SIZE 256.0

@implementation MTOverlayDataSourceFileDirectory

- (id)initWithTileDirectory:(NSString *)tileDirectory {
	if (self = [super init]) {
        tileBase = [tileDirectory retain];
		
        // scan tilePath to determine what files are available
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSDirectoryEnumerator *e = [fileManager enumeratorAtPath:tileDirectory];
        NSString *path = nil;
        NSMutableSet *pathSet = [[NSMutableSet alloc] init];
        NSInteger minZ = INT_MAX;
        while (path = [e nextObject]) {
            if (NSOrderedSame == [[path pathExtension] caseInsensitiveCompare:@"png"]) {
                NSArray *components = [[path stringByDeletingPathExtension] pathComponents];
                if ([components count] == 3) {
                    NSInteger z = [[components objectAtIndex:0] integerValue];
                    NSInteger x = [[components objectAtIndex:1] integerValue];
                    NSInteger y = [[components objectAtIndex:2] integerValue];
                    
                    NSString *tileKey = [[NSString alloc] initWithFormat:@"%d/%d/%d", z, x, y];
                    
                    [pathSet addObject:tileKey];
                    [tileKey release];
                    
                    if (z < minZ)
                        minZ = z;
                }
            }
        }
        
        if ([pathSet count] == 0) {
            NSLog(@"Could not locate any tiles at %@", tileDirectory);
            [pathSet release];
            [self release];
            return nil;
        }
        
        // find bounds of base level of tiles to determine boundingMapRect
        
        NSInteger minX = INT_MAX;
        NSInteger minY = INT_MAX;
        NSInteger maxX = 0;
        NSInteger maxY = 0;
        for (NSString *tileKey in pathSet) {
            NSArray *components = [tileKey pathComponents];
            NSInteger z = [[components objectAtIndex:0] integerValue];
            NSInteger x = [[components objectAtIndex:1] integerValue];
            NSInteger y = [[components objectAtIndex:2] integerValue];
            if (z == minZ) {
                minX = MIN(minX, x);
                minY = MIN(minY, y);
                maxX = MAX(maxX, x);
                maxY = MAX(maxY, y);
            }            
        }
        
		/*
        NSInteger tilesAtZ = pow(2, minZ);
        double sizeAtZ = tilesAtZ * TILE_SIZE;
        double zoomScaleAtMinZ = sizeAtZ / MKMapSizeWorld.width;
        
        // gdal2tiles convention is that the 0th tile in the y direction
        // is at the bottom. MKMapPoint convention is that the 0th point
        // is in the upper left.  So need to flip y to correctly address
        // the tile path.
        NSInteger flippedMinY = abs(minY + 1 - tilesAtZ);
        NSInteger flippedMaxY = abs(maxY + 1 - tilesAtZ);
        
        double x0 = (minX * TILE_SIZE) / zoomScaleAtMinZ;
        double x1 = ((maxX+1) * TILE_SIZE) / zoomScaleAtMinZ;
        double y0 = (flippedMaxY * TILE_SIZE) / zoomScaleAtMinZ;
        double y1 = ((flippedMinY+1) * TILE_SIZE) / zoomScaleAtMinZ;
		*/
		
		NSInteger tilesAtZ = pow(2, minZ);
		double tileWidthInMapPoints = MKMapSizeWorld.width / tilesAtZ;
		
        // gdal2tiles convention is that the 0th tile in the y direction
        // is at the bottom. MKMapPoint convention is that the 0th point
        // is in the upper left.  So need to flip y to correctly address
        // the tile path.
        NSInteger flippedMinY = abs(minY + 1 - tilesAtZ);
        NSInteger flippedMaxY = abs(maxY + 1 - tilesAtZ);

		double x0 = minX * tileWidthInMapPoints;
		double x1 = (maxX + 1) * tileWidthInMapPoints;
		double y0 = flippedMaxY * tileWidthInMapPoints;
		double y1 = (flippedMinY + 1) * tileWidthInMapPoints;
        
        boundingMapRect = MKMapRectMake(x0, y0, x1 - x0, y1 - y0);
        
        tilePaths = pathSet;
    }
    return self;
	
}

- (NSInteger) zoomLevelForZoomScale:(MKZoomScale)zoomScale {
    double numTilesAt1_0 = MKMapSizeWorld.width / TILE_SIZE;
    NSInteger zoomLevelAt1_0 = log2(numTilesAt1_0);  // add 1 because the convention skips a virtual level with 1 tile.
    NSInteger zoomLevel = MAX(0, zoomLevelAt1_0 + floor(log2f(zoomScale) + 0.5));
    return zoomLevel;
}

- (BOOL)hasTilesInMapRect:(MKMapRect)rect zoomScale:(MKZoomScale)scale {
	// Return YES if we have any tiles in the requested rect at the requested scale
	
	NSInteger z = [self zoomLevelForZoomScale:scale];
    
    // Number of tiles wide or high (but not wide * high)
    NSInteger tilesAtZ = pow(2, z);
    
    NSInteger minX = floor((MKMapRectGetMinX(rect) * scale) / TILE_SIZE);
    NSInteger maxX = floor((MKMapRectGetMaxX(rect) * scale) / TILE_SIZE);
    NSInteger minY = floor((MKMapRectGetMinY(rect) * scale) / TILE_SIZE);
    NSInteger maxY = floor((MKMapRectGetMaxY(rect) * scale) / TILE_SIZE);
    
    for (NSInteger x = minX; x <= maxX; x++) {
        for (NSInteger y = minY; y <= maxY; y++) {
            // As in initWithTilePath, need to flip y index to match the gdal2tiles.py convention.
            NSInteger flippedY = abs(y + 1 - tilesAtZ);
            
            NSString *tileKey = [[NSString alloc] initWithFormat:@"%d/%d/%d", z, x, flippedY];
            if ([tilePaths containsObject:tileKey]) {
                [tileKey release];
				return YES;
            }
            [tileKey release];
        }
    }
    
    return NO;
}

- (NSArray *)tilesInMapRect:(MKMapRect)rect zoomScale:(MKZoomScale)scale {
	NSInteger z = [self zoomLevelForZoomScale:scale];
    
    // Number of tiles wide or high (but not wide * high)
    NSInteger tilesAtZ = pow(2, z);
    
    NSInteger minX = floor((MKMapRectGetMinX(rect) * scale) / TILE_SIZE);
    NSInteger maxX = floor((MKMapRectGetMaxX(rect) * scale) / TILE_SIZE);
    NSInteger minY = floor((MKMapRectGetMinY(rect) * scale) / TILE_SIZE);
    NSInteger maxY = floor((MKMapRectGetMaxY(rect) * scale) / TILE_SIZE);
    
    NSMutableArray *tiles = nil;
    
    for (NSInteger x = minX; x <= maxX; x++) {
        for (NSInteger y = minY; y <= maxY; y++) {
            // As in initWithTilePath, need to flip y index to match the gdal2tiles.py convention.
            NSInteger flippedY = abs(y + 1 - tilesAtZ);
            
            NSString *tileKey = [[NSString alloc] initWithFormat:@"%d/%d/%d", z, x, flippedY];
            if ([tilePaths containsObject:tileKey]) {
                if (!tiles) {
                    tiles = [NSMutableArray array];
                }
                
                MKMapRect frame = MKMapRectMake((double)(x * TILE_SIZE) / scale,
                                                (double)(y * TILE_SIZE) / scale,
                                                TILE_SIZE / scale,
                                                TILE_SIZE / scale);
                
                NSString *path = [[NSString alloc] initWithFormat:@"%@/%@.png", tileBase, tileKey];
				UIImage *image = [[UIImage alloc] initWithContentsOfFile:path];
                MTOverlayTile *tile = [[MTOverlayTile alloc] initWithFrame:frame image:image];
                [path release];
                [tiles addObject:tile];
                [tile release];
            }
            [tileKey release];
        }
    }
    
    return tiles;
}

- (MKMapRect)boundingMapRect
{
    return boundingMapRect;
}

@end
